home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / a_utils / _archvrs / unix / unzip51 / shared.c < prev    next >
C/C++ Source or Header  |  1992-11-04  |  19KB  |  563 lines

  1. /*---------------------------------------------------------------------------
  2.  
  3.   shared.c
  4.  
  5.   This file contains general routines shared by both UnZip and ZipInfo.
  6.   [Routines orginally were in unzip.c, zipinfo.c, file_io.c and misc.c.]
  7.  
  8.   ---------------------------------------------------------------------------
  9.  
  10.   Copyrights:  see accompanying file "COPYING" in UnZip source distribution.
  11.  
  12.   ---------------------------------------------------------------------------*/
  13.  
  14.  
  15. #include "unzip.h"
  16. #ifdef MSWIN
  17. #  include "wizunzip.h"
  18. #endif
  19.  
  20.  
  21. /******************************/
  22. /* Function open_input_file() */
  23. /******************************/
  24.  
  25. int open_input_file()    /* return 1 if open failed */
  26. {
  27.     /*
  28.      *  open the zipfile for reading and in BINARY mode to prevent cr/lf
  29.      *  translation, which would corrupt the bitstreams
  30.      */
  31.  
  32. #if defined(UNIX) || defined(TOPS20)
  33.     zipfd = open(zipfn, O_RDONLY);
  34. #else /* !(UNIX || TOPS20) */
  35. #ifdef VMS
  36.     zipfd = open(zipfn, O_RDONLY, 0, "ctx=stm");
  37. #else /* !VMS */
  38. #ifdef MACOS
  39.     zipfd = open(zipfn, 0);
  40. #else /* !MACOS */
  41.     zipfd = open(zipfn, O_RDONLY | O_BINARY);
  42. #endif /* ?MACOS */
  43. #endif /* ?VMS */
  44. #endif /* ?(UNIX || TOPS20) */
  45.     if (zipfd < 1) {
  46.         fprintf(stderr, "error:  can't open zipfile [ %s ]\n", zipfn);
  47.         return 1;
  48.     }
  49.     return 0;
  50.  
  51. } /* end function open_input_file() */
  52.  
  53.  
  54.  
  55.  
  56.  
  57. /**********************/
  58. /* Function readbuf() */
  59. /**********************/
  60.  
  61. int readbuf(buf, size)   /* return number of bytes read into buf */
  62.     char *buf;
  63.     register unsigned size;
  64. {
  65.     register int count;
  66.     int n;
  67.  
  68.     n = size;
  69.     while (size) {
  70.         if (incnt == 0) {
  71.             if ((incnt = read(zipfd, (char *)inbuf, INBUFSIZ)) <= 0)
  72.                 return (n-size);
  73.             /* buffer ALWAYS starts on a block boundary:  */
  74.             cur_zipfile_bufstart += INBUFSIZ;
  75.             inptr = inbuf;
  76.         }
  77.         count = MIN(size, (unsigned)incnt);
  78.         memcpy(buf, inptr, count);
  79.         buf += count;
  80.         inptr += count;
  81.         incnt -= count;
  82.         size -= count;
  83.     }
  84.     return n;
  85.  
  86. } /* end function readbuf() */
  87.  
  88.  
  89.  
  90.  
  91.  
  92. /***********************************/
  93. /* Function find_end_central_dir() */
  94. /***********************************/
  95.  
  96. int find_end_central_dir(searchlen)   /* return PK-class error */
  97.     longint searchlen;
  98. {
  99.     int i, numblks, found=FALSE;
  100.     longint tail_len;
  101.     ec_byte_rec byterec;
  102.  
  103.  
  104. /*---------------------------------------------------------------------------
  105.     Treat case of short zipfile separately.
  106.   ---------------------------------------------------------------------------*/
  107.  
  108.     if (ziplen <= INBUFSIZ) {
  109.         lseek(zipfd, 0L, SEEK_SET);
  110.         if ((incnt = read(zipfd,(char *)inbuf,(unsigned int)ziplen)) ==
  111.              (int)ziplen)
  112.  
  113.             /* 'P' must be at least 22 bytes from end of zipfile */
  114.             for (inptr = inbuf+(int)ziplen-22;  inptr >= inbuf;  --inptr)
  115.                 if ((ascii_to_native(*inptr) == 'P')  &&
  116.                      !strncmp((char *)inptr, end_central_sig, 4)) {
  117.                     incnt -= inptr - inbuf;
  118.                     found = TRUE;
  119.                     break;
  120.                 }
  121.  
  122. /*---------------------------------------------------------------------------
  123.     Zipfile is longer than INBUFSIZ:  may need to loop.  Start with short
  124.     block at end of zipfile (if not TOO short).
  125.   ---------------------------------------------------------------------------*/
  126.  
  127.     } else {
  128.         if ((tail_len = ziplen % INBUFSIZ) > ECREC_SIZE) {
  129.             cur_zipfile_bufstart = lseek(zipfd, ziplen-tail_len, SEEK_SET);
  130.             if ((incnt = read(zipfd,(char *)inbuf,(unsigned int)tail_len)) !=
  131.                  (int)tail_len)
  132.                 goto fail;      /* shut up; it's expedient */
  133.  
  134.             /* 'P' must be at least 22 bytes from end of zipfile */
  135.             for (inptr = inbuf+(int)tail_len-22;  inptr >= inbuf;  --inptr)
  136.                 if ((ascii_to_native(*inptr) == 'P')  &&
  137.                      !strncmp((char *)inptr, end_central_sig, 4)) {
  138.                     incnt -= inptr - inbuf;
  139.                     found = TRUE;
  140.                     break;
  141.                 }
  142.             /* sig may span block boundary: */
  143.             strncpy((char *)hold, (char *)inbuf, 3);
  144.         } else
  145.             cur_zipfile_bufstart = ziplen - tail_len;
  146.  
  147.     /*-----------------------------------------------------------------------
  148.         Loop through blocks of zipfile data, starting at the end and going
  149.         toward the beginning.  In general, need not check whole zipfile for
  150.         signature, but may want to do so if testing.
  151.       -----------------------------------------------------------------------*/
  152.  
  153.         numblks = (int)((searchlen - tail_len + (INBUFSIZ-1)) / INBUFSIZ);
  154.         /*               ==amount=   ==done==   ==rounding==    =blksiz=  */
  155.  
  156.         for (i = 1;  !found && (i <= numblks);  ++i) {
  157.             cur_zipfile_bufstart -= INBUFSIZ;
  158.             lseek(zipfd, cur_zipfile_bufstart, SEEK_SET);
  159.             if ((incnt = read(zipfd,(char *)inbuf,INBUFSIZ)) != INBUFSIZ)
  160.                 break;          /* fall through and fail */
  161.  
  162.             for (inptr = inbuf+INBUFSIZ-1;  inptr >= inbuf;  --inptr)
  163.                 if ((ascii_to_native(*inptr) == 'P')  &&
  164.                      !strncmp((char *)inptr, end_central_sig, 4)) {
  165.                     incnt -= inptr - inbuf;
  166.                     found = TRUE;
  167.                     break;
  168.                 }
  169.             /* sig may span block boundary: */
  170.             strncpy((char *)hold, (char *)inbuf, 3);
  171.         }
  172.     } /* end if (ziplen > INBUFSIZ) */
  173.  
  174. /*---------------------------------------------------------------------------
  175.     Searched through whole region where signature should be without finding
  176.     it.  Print informational message and die a horrible death.
  177.   ---------------------------------------------------------------------------*/
  178.  
  179. fail:
  180.     if (!found) {
  181. #ifdef MSWIN
  182.         MessageBeep(1);
  183. #endif
  184.         fprintf(stderr, "  %s:\n\n\
  185.   End-of-central-directory signature not found.  Either this file is not\n\
  186.   a zipfile, or it constitutes one disk of a multi-part archive.  In the\n\
  187.   latter case the central directory and zipfile comment will be found on\n\
  188.   the last disk(s) of this archive.\n", zipfn);
  189.         return PK_ERR;   /* failed */
  190.     }
  191.  
  192. /*---------------------------------------------------------------------------
  193.     Found the signature, so get the end-central data before returning.  Do
  194.     any necessary machine-type conversions (byte ordering, structure padding
  195.     compensation) by reading data into character array and copying to struct.
  196.   ---------------------------------------------------------------------------*/
  197.  
  198.     real_ecrec_offset = cur_zipfile_bufstart + (inptr-inbuf);
  199. #ifdef TEST
  200.     printf("\n  found end-of-central-dir signature at offset %ld (%.8lXh)\n",
  201.       real_ecrec_offset, real_ecrec_offset);
  202.     printf("    from beginning of file; offset %d (%.4Xh) within block\n",
  203.       inptr-inbuf, inptr-inbuf);
  204. #endif
  205.  
  206.     if (readbuf((char *) byterec, ECREC_SIZE+4) <= 0)
  207.         return PK_EOF;
  208.  
  209.     ecrec.number_this_disk =
  210.         makeword(&byterec[NUMBER_THIS_DISK]);
  211.     ecrec.num_disk_with_start_central_dir =
  212.         makeword(&byterec[NUM_DISK_WITH_START_CENTRAL_DIR]);
  213.     ecrec.num_entries_centrl_dir_ths_disk =
  214.         makeword(&byterec[NUM_ENTRIES_CENTRL_DIR_THS_DISK]);
  215.     ecrec.total_entries_central_dir =
  216.         makeword(&byterec[TOTAL_ENTRIES_CENTRAL_DIR]);
  217.     ecrec.size_central_directory =
  218.         makelong(&byterec[SIZE_CENTRAL_DIRECTORY]);
  219.     ecrec.offset_start_central_directory =
  220.         makelong(&byterec[OFFSET_START_CENTRAL_DIRECTORY]);
  221.     ecrec.zipfile_comment_length =
  222.         makeword(&byterec[ZIPFILE_COMMENT_LENGTH]);
  223.  
  224.     expect_ecrec_offset = ecrec.offset_start_central_directory +
  225.                           ecrec.size_central_directory;
  226.     return PK_COOL;
  227.  
  228. } /* end function find_end_central_dir() */
  229.  
  230.  
  231.  
  232.  
  233.  
  234. /********************************/
  235. /* Function get_cdir_file_hdr() */
  236. /********************************/
  237.  
  238. int get_cdir_file_hdr()   /* return PK-type error code */
  239. {
  240.     cdir_byte_hdr byterec;
  241.  
  242.  
  243. /*---------------------------------------------------------------------------
  244.     Read the next central directory entry and do any necessary machine-type
  245.     conversions (byte ordering, structure padding compensation--do so by
  246.     copying the data from the array into which it was read (byterec) to the
  247.     usable struct (crec)).
  248.   ---------------------------------------------------------------------------*/
  249.  
  250.     if (readbuf((char *)byterec, CREC_SIZE) <= 0)
  251.         return PK_EOF;
  252.  
  253.     crec.version_made_by[0] = byterec[C_VERSION_MADE_BY_0];
  254.     crec.version_made_by[1] = byterec[C_VERSION_MADE_BY_1];
  255.     crec.version_needed_to_extract[0] = byterec[C_VERSION_NEEDED_TO_EXTRACT_0];
  256.     crec.version_needed_to_extract[1] = byterec[C_VERSION_NEEDED_TO_EXTRACT_1];
  257.  
  258.     crec.general_purpose_bit_flag =
  259.         makeword(&byterec[C_GENERAL_PURPOSE_BIT_FLAG]);
  260.     crec.compression_method =
  261.         makeword(&byterec[C_COMPRESSION_METHOD]);
  262.     crec.last_mod_file_time =
  263.         makeword(&byterec[C_LAST_MOD_FILE_TIME]);
  264.     crec.last_mod_file_date =
  265.         makeword(&byterec[C_LAST_MOD_FILE_DATE]);
  266.     crec.crc32 =
  267.         makelong(&byterec[C_CRC32]);
  268.     crec.csize =
  269.         makelong(&byterec[C_COMPRESSED_SIZE]);
  270.     crec.ucsize =
  271.         makelong(&byterec[C_UNCOMPRESSED_SIZE]);
  272.     crec.filename_length =
  273.         makeword(&byterec[C_FILENAME_LENGTH]);
  274.     crec.extra_field_length =
  275.         makeword(&byterec[C_EXTRA_FIELD_LENGTH]);
  276.     crec.file_comment_length =
  277.         makeword(&byterec[C_FILE_COMMENT_LENGTH]);
  278.     crec.disk_number_start =
  279.         makeword(&byterec[C_DISK_NUMBER_START]);
  280.     crec.internal_file_attributes =
  281.         makeword(&byterec[C_INTERNAL_FILE_ATTRIBUTES]);
  282.     crec.external_file_attributes =
  283.         makelong(&byterec[C_EXTERNAL_FILE_ATTRIBUTES]);  /* LONG, not word! */
  284.     crec.relative_offset_local_header =
  285.         makelong(&byterec[C_RELATIVE_OFFSET_LOCAL_HEADER]);
  286.  
  287.     return PK_COOL;
  288.  
  289. } /* end function get_cdir_file_hdr() */
  290.  
  291.  
  292.  
  293.  
  294.  
  295. /************************/
  296. /* Function do_string() */
  297. /************************/
  298.  
  299. int do_string(len, option)      /* return PK-type error code */
  300.     unsigned int len;           /* without prototype, ush converted to this */
  301.     int option;
  302. {
  303.     int block_length, error=PK_COOL;
  304.     ush comment_bytes_left, extra_len;
  305.  
  306.  
  307. /*---------------------------------------------------------------------------
  308.     This function processes arbitrary-length (well, usually) strings.  Three
  309.     options are allowed:  SKIP, wherein the string is skipped pretty logical,
  310.     eh?); DISPLAY, wherein the string is printed to standard output after un-
  311.     dergoing any necessary or unnecessary character conversions; and FILENAME,
  312.     wherein the string is put into the filename[] array after undergoing ap-
  313.     propriate conversions (including case-conversion, if that is indicated:
  314.     see the global variable pInfo->lcflag).  The latter option should be OK,
  315.     since filename is now dimensioned at 1025, but we check anyway.
  316.  
  317.     The string, by the way, is assumed to start at the current file-pointer
  318.     position; its length is given by len.  So start off by checking length
  319.     of string:  if zero, we're already set.
  320.   ---------------------------------------------------------------------------*/
  321.  
  322.     if (!len)
  323.         return PK_COOL;
  324.  
  325.     switch (option) {
  326.  
  327.     /*
  328.      * First case:  print string on standard output.  First set loop vari-
  329.      * ables, then loop through the comment in chunks of OUTBUFSIZ bytes,
  330.      * converting formats and printing as we go.  The second half of the
  331.      * loop conditional was added because the file might be truncated, in
  332.      * which case comment_bytes_left will remain at some non-zero value for
  333.      * all time.  outbuf is used as a scratch buffer because it is avail-
  334.      * able (we should be either before or in between any file processing).
  335.      * [The typecast in front of the MIN() macro was added because of the
  336.      * new promotion rules under ANSI C; readbuf() wants an int, but MIN()
  337.      * returns a signed long, if I understand things correctly.  The proto-
  338.      * type should handle it, but just in case...]
  339.      */
  340.  
  341.     case DISPLAY:
  342.         comment_bytes_left = len;
  343.         block_length = OUTBUFSIZ;    /* for the while statement, first time */
  344.         while (comment_bytes_left > 0 && block_length > 0) {
  345.             if ((block_length = readbuf((char *)outbuf,
  346.                    (int) MIN(OUTBUFSIZ, comment_bytes_left))) <= 0)
  347.                 return PK_EOF;
  348.             comment_bytes_left -= block_length;
  349.             NUKE_CRs(outbuf, block_length);     /* (modifies block_length) */
  350.  
  351.             /*  this is why we allocated an extra byte for outbuf: */
  352.             outbuf[block_length] = '\0';        /* terminate w/zero:  ASCIIZ */
  353.  
  354.             A_TO_N(outbuf);     /* translate string to native */
  355.  
  356. #ifdef MSWIN
  357.             /* ran out of local mem -- had to cheat */
  358.             WriteStringToMsgWin(outbuf, bRealTimeMsgUpdate);
  359. #else /* !MSWIN */
  360.             printf("%s", outbuf);
  361. #endif /* ?MSWIN */
  362.         }
  363.         printf("\n");   /* assume no newline at end */
  364.         break;
  365.  
  366.     /*
  367.      * Second case:  read string into filename[] array.  The filename should
  368.      * never ever be longer than FILNAMSIZ-1 (1024), but for now we'll check,
  369.      * just to be sure.
  370.      */
  371.  
  372.     case FILENAME:
  373.         extra_len = 0;
  374.         if (len >= FILNAMSIZ) {
  375.             fprintf(stderr, "warning:  filename too long--truncating.\n");
  376.             error = PK_WARN;
  377.             extra_len = len - FILNAMSIZ + 1;
  378.             len = FILNAMSIZ - 1;
  379.         }
  380.         if (readbuf(filename, len) <= 0)
  381.             return PK_EOF;
  382.         filename[len] = '\0';   /* terminate w/zero:  ASCIIZ */
  383.  
  384.         A_TO_N(filename);       /* translate string to native */
  385.  
  386.         if (pInfo->lcflag)      /* replace with lowercase filename */
  387.             TOLOWER(filename, filename);
  388.  
  389.         if (!extra_len)         /* we're done here */
  390.             break;
  391.  
  392.         /*
  393.          * We truncated the filename, so print what's left and then fall
  394.          * through to the SKIP routine.
  395.          */
  396.         fprintf(stderr, "[ %s ]\n", filename);
  397.         len = extra_len;
  398.         /*  FALL THROUGH...  */
  399.  
  400.     /*
  401.      * Third case:  skip string, adjusting readbuf's internal variables
  402.      * as necessary (and possibly skipping to and reading a new block of
  403.      * data).
  404.      */
  405.  
  406.     case SKIP:
  407.         LSEEK(cur_zipfile_bufstart + (inptr-inbuf) + len)
  408.         break;
  409.  
  410.     /*
  411.      * Fourth case:  assume we're at the start of an "extra field"; malloc
  412.      * storage for it and read data into the allocated space.
  413.      */
  414.  
  415.     case EXTRA_FIELD:
  416.         if (extra_field != (uch *)NULL)
  417.             free(extra_field);
  418.         if ((extra_field = (uch *)malloc(len)) == (uch *)NULL) {
  419.             fprintf(stderr,
  420.               "warning:  extra field too long (%d).  Ignoring...\n", len);
  421.             LSEEK(cur_zipfile_bufstart + (inptr-inbuf) + len)
  422.         } else
  423.             if (readbuf((char *)extra_field, len) <= 0)
  424.                 return PK_EOF;
  425.         break;
  426.  
  427.     } /* end switch (option) */
  428.     return error;
  429.  
  430. } /* end function do_string() */
  431.  
  432.  
  433.  
  434.  
  435.  
  436. #ifdef EBCDIC
  437.  
  438. /*
  439.  * This is the MTS ASCII->EBCDIC translation table. It provides a 1-1
  440.  * translation from ISO 8859/1 8-bit ASCII to IBM Code Page 37 EBCDIC.
  441.  */
  442.  
  443. unsigned char ebcdic[] =
  444. {
  445.     0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f,
  446.     0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
  447.     0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26,
  448.     0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f,
  449.     0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d,
  450.     0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61,
  451.     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
  452.     0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f,
  453.     0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
  454.     0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
  455.     0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
  456.     0xe7, 0xe8, 0xe9, 0xba, 0xe0, 0xbb, 0xb0, 0x6d,
  457.     0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
  458.     0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
  459.     0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6,
  460.     0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0xa1, 0x07,
  461.     0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17,
  462.     0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b,
  463.     0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08,
  464.     0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xff,
  465.     0x41, 0xaa, 0x4a, 0xb1, 0x9f, 0xb2, 0x6a, 0xb5,
  466.     0xbd, 0xb4, 0x9a, 0x8a, 0x5f, 0xca, 0xaf, 0xbc,
  467.     0x90, 0x8f, 0xea, 0xfa, 0xbe, 0xa0, 0xb6, 0xb3,
  468.     0x9d, 0xda, 0x9b, 0x8b, 0xb7, 0xb8, 0xb9, 0xab,
  469.     0x64, 0x65, 0x62, 0x66, 0x63, 0x67, 0x9e, 0x68,
  470.     0x74, 0x71, 0x72, 0x73, 0x78, 0x75, 0x76, 0x77,
  471.     0xac, 0x69, 0xed, 0xee, 0xeb, 0xef, 0xec, 0xbf,
  472.     0x80, 0xfd, 0xfe, 0xfb, 0xfc, 0xad, 0xae, 0x59,
  473.     0x44, 0x45, 0x42, 0x46, 0x43, 0x47, 0x9c, 0x48,
  474.     0x54, 0x51, 0x52, 0x53, 0x58, 0x55, 0x56, 0x57,
  475.     0x8c, 0x49, 0xcd, 0xce, 0xcb, 0xcf, 0xcc, 0xe1,
  476.     0x70, 0xdd, 0xde, 0xdb, 0xdc, 0x8d, 0x8e, 0xdf
  477. }; /* end ebcdic[] */
  478.  
  479. #endif                          /* EBCDIC */
  480.  
  481.  
  482.  
  483.  
  484.  
  485. /***********************/
  486. /* Function makeword() */
  487. /***********************/
  488.  
  489. ush makeword(b)
  490.     uch *b;
  491. {
  492.     /*
  493.      * Convert Intel style 'short' integer to non-Intel non-16-bit
  494.      * host format.  This routine also takes care of byte-ordering.
  495.      */
  496.     return ((b[1] << 8) | b[0]);
  497. }
  498.  
  499.  
  500.  
  501.  
  502.  
  503. /***********************/
  504. /* Function makelong() */
  505. /***********************/
  506.  
  507. ulg makelong(sig)
  508.     uch *sig;
  509. {
  510.     /*
  511.      * Convert intel style 'long' variable to non-Intel non-16-bit
  512.      * host format.  This routine also takes care of byte-ordering.
  513.      */
  514.     return (((ulg)sig[3]) << 24)
  515.         + (((ulg)sig[2]) << 16)
  516.         + (((ulg)sig[1]) << 8)
  517.         + ((ulg)sig[0]);
  518. }
  519.  
  520.  
  521.  
  522.  
  523.  
  524. #ifdef ZMEM   /* memset, memcpy for systems without them */
  525.  
  526. /*********************/
  527. /* Function memset() */
  528. /*********************/
  529.  
  530. char *memset(buf, init, len)
  531.     register char *buf, init;   /* buffer loc and initializer */
  532.     register unsigned int len;  /* length of the buffer */
  533. {
  534.     char *start;
  535.  
  536.     start = buf;
  537.     while (len--)
  538.         *(buf++) = init;
  539.     return start;
  540. }
  541.  
  542.  
  543.  
  544.  
  545.  
  546. /*********************/
  547. /* Function memcpy() */
  548. /*********************/
  549.  
  550. char *memcpy(dst, src, len)
  551.     register char *dst, *src;
  552.     register unsigned int len;
  553. {
  554.     char *start;
  555.  
  556.     start = dst;
  557.     while (len-- > 0)
  558.         *dst++ = *src++;
  559.     return start;
  560. }
  561.  
  562. #endif /* ZMEM */
  563.